#include "stdafx.h"
#include "ClassEntryMgr.h"

#include "IXBaseObj.h"
#include "Util.h"

CClassEntryMgr::CClassEntryMgr()
{
    ;
}

CClassEntryMgr::~CClassEntryMgr()
{
}

HRESULT CClassEntryMgr::defRegisterClassEntry(const stIntfEntry* pIntfEntry, const stClassEntry* pClassEntry)
{
    size_t nIntfIndex = 0;
    while(!IsEqualGUID(pIntfEntry[nIntfIndex].intfGuid, GUID_NULL))
    {
        stClassEntryIndex index = {pIntfEntry[nIntfIndex].intfGuid, pClassEntry->clsid};
        stClassEntryData  data = {&pIntfEntry[nIntfIndex], pClassEntry};

#ifdef _DEBUG
        ClassEntryMap::iterator IteEntry = m_ClassEntryMap.find(index);
        XASSERT(ASSERT_LEVEL_ERROR, (pIntfEntry[nIntfIndex].nFlags & eIntf_NoCreate) || IteEntry == m_ClassEntryMap.end(), _T("Interface Defined Twice!"));
        if(IteEntry != m_ClassEntryMap.end())
            XOutputLog1(_T("ClassEntryMgr"), _T("Intf: %s, Class: %s"), pIntfEntry[nIntfIndex].strIntfName, pClassEntry->strClassName);
#endif // _DEBUG

        if((pIntfEntry[nIntfIndex].nFlags & eIntf_NoCreate) == 0)
            m_ClassEntryMap.insert(std::pair<stClassEntryIndex, stClassEntryData>(index, data));
        ++ nIntfIndex;
    }
    return S_OK;
}

HRESULT CClassEntryMgr::CreateObject(REFGUID intfIID, REFCLSID refCLSID, IXBaseObj** pObj)
{
    Util::SkinCoreStatus status = Util::GetSkinCoreStatus();
    if(status != Util::CoreStatus_Ready)
    {
        return E_FAIL;
    }

    BOOL bXSkinGUID = IsEqualGUID(refCLSID, GUID_NULL);
    const CLSID* pCLSID = bXSkinGUID ? &gs_GuidXSkin : &refCLSID;

    stClassEntryIndex index = {intfIID, *pCLSID};
    ClassEntryMap::iterator IteEntry = m_ClassEntryMap.find(index);

    if(IteEntry == m_ClassEntryMap.end())
        return E_NOINTERFACE;

    HRESULT hResult = E_FAIL;
    const stClassEntryData& data = IteEntry->second;
    if(data.pIntfEntry->nFlags & eIntf_Singleton)
    {
        hResult = GetSkinObjectHelper(intfIID, *pCLSID, pObj);
    }
    else
    {
        hResult = CreateObjectHelper(*pCLSID, intfIID, pObj);
    }

    return hResult;
}

HRESULT CClassEntryMgr::GetSkinObjectHelper(REFGUID intfIID, REFCLSID clsid, IXBaseObj** pObj)
{
    *pObj = NULL;
    HRESULT hResult = E_FAIL;

    stClassEntryIndex index = {intfIID, clsid};
    SingletonObjectStoreMap::iterator IteObj = m_SingletonObjectStoreMap.find(index);
    if(IteObj == m_SingletonObjectStoreMap.end())
    {
        hResult = CreateObjectHelper(clsid, intfIID, pObj);
        std::pair<SingletonObjectStoreMap::iterator, bool> result = m_SingletonObjectStoreMap.insert(SingletonObjectStoreMap::value_type(index, (*pObj)));

        if(result.second)
        {
            IteObj = result.first;
        }
        else
        {
            (*pObj)->Release();
            hResult = E_FAIL;
        }
    }
    else
    {
        (*pObj) = IteObj->second;
        hResult = S_OK;
    }

    if(SUCCEEDED(hResult))
    {
        (*pObj)->AddRef();
    }
    return hResult;
}

// This method will increase ref count of pObj.
HRESULT CClassEntryMgr::CreateObjectHelper(REFCLSID refCLSID, REFGUID intfIID, IXBaseObj** pObj)
{
    stClassEntryIndex index = {intfIID, refCLSID};
    ClassEntryMap::iterator IteEntry = m_ClassEntryMap.find(index);

    if(IteEntry == m_ClassEntryMap.end())
        return E_NOINTERFACE;

    stClassEntryData&   data = IteEntry->second;

    const stClassEntry* const& pClassEntry = data.pClassEntry;
    const stIntfEntry* const& pIntfEntry = data.pIntfEntry;

    const ObjCreateFunc& pfnCreator = pClassEntry->pfnCreator;
    if(pfnCreator == NULL)
        return E_INVALIDARG;

    return pfnCreator(intfIID, pObj);
}

void CClassEntryMgr::UnInitClassEntryMgr()
{
    m_ClassEntryMap.clear();

    SingletonObjectStoreMap::iterator IteIntf = m_SingletonObjectStoreMap.begin();
    for(; IteIntf != m_SingletonObjectStoreMap.end(); ++ IteIntf)
    {
        const stClassEntryIndex& index = IteIntf->first;
        IXBaseObj*& pObj = IteIntf->second;
        pObj->Release();
    }
    m_SingletonObjectStoreMap.clear();
}
